package com.wgo.bpot.server.persist;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.log4j.Logger;

import com.wgo.bpot.common.transport.exception.db.DatabaseException;



public class UniqueValueFactoryImpl extends BasicUniqueValueFactory {
	private static final transient Logger log = Logger.getLogger(UniqueValueFactoryImpl.class);
	private Map<String,AtomicLong> valueMap = Collections.synchronizedMap(new HashMap<String,AtomicLong>());
	private Map<String,AtomicLong> reservedMap = Collections.synchronizedMap(new HashMap<String,AtomicLong>());
	private static final transient int BUFFER_SIZE = 50;
	

	/* (non-Javadoc)
	 * @see remato.server2.util.UniqueValueFactory#getNextValueAndIncrement(java.lang.String)
	 */
	public Long getNextValueAndIncrement(String entityType){
		Long value = null;
		AtomicLong al = valueMap.get(entityType);
		if (null != al) {
			value = new Long(al.getAndIncrement());
		}
		if ((null == value) || (value.longValue() == reservedMap.get(entityType).get())) {
			reserveValue(entityType);
			value = getNextValueAndIncrement(entityType);
		}
		log.debug("Returning unique value for " + entityType + ": " + value);
		return value;
	}
	
	private synchronized void reserveValue(String entityType){
		PersistService persistService = getPersistService();
//		PersistTransaction transaction = session.startTransaction();
		UniqueValue uv = (UniqueValue)persistService.find(getUniqueValueClass(),"entityType",entityType, 1);
		if (null == uv) {
			try {
				uv = (UniqueValue) persistService.newInstance(getUniqueValueClass());
			} catch (Exception e) {
				throw new DatabaseException("Unable to insantiate class " + getUniqueValueClass().getName(),e);
			}
			uv.setEntityType(entityType);
			uv.setNextValue(0);
			log.debug("Created new entityType '" + entityType + "'");
		}
		valueMap.put(uv.getEntityType(),new AtomicLong(uv.getNextValue()));
		long resVal = uv.getNextValue() + BUFFER_SIZE;
		uv.setNextValue(resVal);
		reservedMap.put(uv.getEntityType(),new AtomicLong(resVal));
		persistService.saveOrUpdate(uv);
//		transaction.commit();
		log.debug("Reserved new unique value for " + entityType + ": " + resVal);
	}

	public Queue<Long> getNextReservedValues(String entityType, int noValues) {
		Queue<Long> result = new PriorityQueue<Long>(noValues);
		for (int i = 0; i < noValues; i++) {
			result.offer(getNextValueAndIncrement(entityType));
		}
		return result;
	}
}
